#include "Components/ComponentClothing.h"
#include "Components/ComponentVitalStats.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentPlayer.h"

#include "Managers/BlocksManager.h"

#include "Subsystems/SubsystemInventories.h"
#include "Subsystems/SubsystemPickables.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemTimeOfDay.h"

#include "Player/PlayerData.h"
#include "Blocks/AirBlock.h"
#include "Blocks/ClothingBlock.h"

#include "GameRandom.hpp"
#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

namespace PocketSurvival
{
    uint32_t ComponentClothing::slotsCount()
    {
        return 4;
    }
    void ComponentClothing::setSlotValue(uint32_t slotIndex, int32_t cellValue, uint32_t valueCount)
    {
        throw std::logic_error("ComponentClothing 的 setSlotValue 函数不应该被调用");
    }

    /***********************************************************************************************************
     * @brief 获取对应位置最外层的衣服
     * @param slotIndex 位置
     * @return 衣服id,没有的话为0
     ***********************************************************************************************************/
    int32_t ComponentClothing::getSlotValue(uint32_t slotIndex)
    {
        // 衣服的存储就如同一个栈，先进的后出
        std::vector<int32_t> clothingVec = getClothes((ClothingSlot)slotIndex);
        if (clothingVec.size() > 0)
        {
            return clothingVec[clothingVec.size() - 1];
        }
        return 0;
    }

    /***********************************************************************************************************
     * @brief 判断对应位置是否有衣服
     * @param slotIndex 位置
     * @return 有的话为1，没有为0
     ***********************************************************************************************************/
    uint32_t ComponentClothing::getSlotCount(uint32_t slotIndex)
    {
        std::vector<int32_t> clothingVec = getClothes((ClothingSlot)slotIndex);
        if (clothingVec.size() > 0)
        {
            return 1;
        }
        return 0;
    }

    void ComponentClothing::onSlotChange(uint32_t slotIndex)
    {
        GameSingleton::singletonPtr->getSingleton<SubsystemInventories>()->pushSyncItem(this, slotIndex);
    }

    uint32_t ComponentClothing::getSlotCapacity(uint32_t slotIndex, int32_t cellValue)
    {
        return 0;
    }

    uint32_t ComponentClothing::getSlotProcessCapacity(uint32_t slotIndex, int32_t cellValue)
    {
        Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(cellValue)];
        if (block->getNutritionalValue(cellValue) > 0.0f)
        {
            return 1;
        }
        if (canWearClothing(cellValue) == true)
        {
            return 1;
        }
        return 0;
    }

    void ComponentClothing::addSlotItems(uint32_t slotIndex, int32_t cellValue, uint32_t count)
    {
    }

    bool ComponentClothing::addNetSlotItems(uint32_t slotIndex, int32_t cellValue, uint32_t count)
    {
        return false;
    }

    void ComponentClothing::processSlotItems(IInventory *sourceInventory, uint32_t sourceSlotIndex, uint32_t slotIndex, int32_t cellValue, uint32_t count, uint32_t processCount, int32_t &outProcessedValue, uint32_t &outProcessedCount)
    {
        outProcessedCount = 0;
        outProcessedValue = 0;
        if (processCount != 1)
        {
            return;
        }
        Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(cellValue)];
        if (block->getNutritionalValue(cellValue) > 0.0f)
        {
            block->getEatRamins(outProcessedValue, outProcessedCount);
            if (count > 1 && outProcessedCount > 0 && outProcessedValue != cellValue)
            {
                outProcessedValue = cellValue;
                outProcessedCount = processCount;
            }
            else if (componentVitalStats->eat(sourceInventory, sourceSlotIndex, cellValue) == false)
            {
                outProcessedValue = cellValue;
                outProcessedCount = processCount;
            }
        }
        else
        {
            ClothingBlock *clothingBlock = block->asBlock<ClothingBlock *>();
            if (clothingBlock != nullptr)
            {
                std::lock_guard<std::mutex> lk(clothesMutex);

                const ClothingData &clothingData = clothingBlock->getClothingData(Terrain::ExtractData(cellValue));
                uint8_t slotIndex = (uint8_t)clothingData.Slot;
                clothes[slotIndex].push_back(cellValue);
                onSlotChange(slotIndex);
                updateAttribute();
            }
        }
    }

    uint32_t ComponentClothing::removeSlotItems(uint32_t slotIndex, uint32_t count)
    {
        int32_t removeCount = removeNetSlotItems(slotIndex, count);
        onSlotChange(slotIndex);
        return removeCount;
    }

    uint32_t ComponentClothing::removeNetSlotItems(uint32_t slotIndex, uint32_t count)
    {
        std::lock_guard<std::mutex> lk(clothesMutex);
        if (1 == count)
        {
            if (clothes[slotIndex].size() > 0)
            {
                clothes[slotIndex].pop_back();
                updateAttribute();
                return 1;
            }
        }
        return 0;
    }

    void ComponentClothing::dropAllItems(const Vector3 &position)
    {
        std::lock_guard<std::mutex> lk(clothesMutex);
        GameRandom *random = GameSingleton::gameRandom;
        for (int32_t index = 0; index < clothes.size(); ++index)
        {
            for (int32_t clothValue : clothes[index])
            {
                std::shared_ptr<Vector3> velocity = std::make_shared<Vector3>(random->getFloat(5.0f, 10.0f) * Vector3::Normalize(Vector3(random->getFloat(-1.0f, 1.0f), random->getFloat(1.0f, 2.0f), random->getFloat(-1.0f, 1.0f))));
                GameSingleton::singletonPtr->getSingleton<SubsystemPickables>()->addPickable(clothValue, 1, position, velocity, nullptr);
            }
            clothes[index].clear();
            onSlotChange(index);
        }
        updateAttribute();
    }

    void ComponentClothing::dropSlotItems(uint32_t slotIndex, const Vector3 &position, const Vector3 &velocity)
    {
    }

    void ComponentClothing::setClothes(ClothingSlot slot, const std::vector<int32_t> &theClothes)
    {
        std::lock_guard<std::mutex> lk(clothesMutex);
        std::vector<int32_t> &clothVec = clothes[(uint8_t)slot];
        bool isEqual = theClothes.size() == clothVec.size();
        if (isEqual == true)
        {
            for (int32_t index = 0; index < clothVec.size(); ++index)
            {
                if (clothVec[index] != theClothes[index])
                {
                    isEqual = false;
                    break;
                }
            }
        }
        if (isEqual == false)
        {
            clothVec.clear();
            for (int32_t value : theClothes)
            {
                clothVec.push_back(value);
            }
            onSlotChange((uint32_t)slot);
            updateAttribute();
        }
    }

    void ComponentClothing::updateAttribute()
    {
        float num = 0.0f;
        steedMovementSpeedFactor = 1.0f;

        float numBuff[4] = {
            2.0f,
            0.2f,
            0.4f,
            2.0f};
        for (uint8_t index = 0; index < clothes.size(); ++index)
        {
            for (int32_t item : clothes[index])
            {
                ClothingBlock *clothingBlock = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(item)]->asBlock<ClothingBlock *>();
                if (clothingBlock != nullptr)
                {
                    const ClothingData &clothingData = clothingBlock->getClothingData(Terrain::ExtractData(item));
                    num += clothingData.DensityModifier;

                    numBuff[index] += clothingData.Insulation;
                    steedMovementSpeedFactor += clothingData.SteedMovementSpeedFactor;
                }
            }
        }
        componentBody->density = componentBody->defaultDensity + num;

        insulation = 1.0f / (1.0f / numBuff[0] + 1.0f / numBuff[1] + 1.0f / numBuff[2] + 1.0f / numBuff[3]);
        // leastInsulatedSlot = (ClothingSlot)MathUtils::MinIndex<float>(numBuff, 4);
    }

    float ComponentClothing::applyArmorProtection(float attackPower)
    {
        float num = GameSingleton::gameRandom->getFloat(0.0f, 1.0f);
        ClothingSlot slot = (num < 0.1f) ? ClothingSlot::Feet : ((num < 0.3f) ? ClothingSlot::Legs : ((num < 0.95f) ? ClothingSlot::Torso : ClothingSlot::Head));

        float num2 = GameSingleton::blocksManager->blocks[ClothingBlock::Index()]->Durability + 1;

        std::vector<int32_t> clothVec = getClothes(slot);
        for (int32_t i = 0; i < clothVec.size(); i++)
        {
            int32_t value = clothVec[i];
            ClothingBlock *clothingBlock = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(value)]->asBlock<ClothingBlock *>();
            const ClothingData &clothingData = clothingBlock->getClothingData(Terrain::ExtractData(value));
            float x = (num2 - clothingBlock->getDamage(value)) / num2 * clothingData.Sturdiness;

            float num3 = MathUtils::Min<float>(attackPower * MathUtils::Saturate(clothingData.ArmorProtection), x);
            if (num3 > 0.0f)
            {
                attackPower -= num3;
                if (componentPlayer->playerData->gameMode != GameMode::Creative)
                {
                    float x2 = num3 / clothingData.Sturdiness * num2 + 0.001f;
                    int32_t damageCount = (int32_t)(MathUtils::Floor<float>(x2) + (GameSingleton::gameRandom->getBool(MathUtils::Remainder(x2, 1.0f)) ? 1 : 0));
                    clothVec[i] = GameSingleton::blocksManager->damageItem(value, damageCount);
                }
            }
        }

        std::vector<int32_t> newClothVec;
        for (int32_t value : clothVec)
        {
            if (Terrain::ExtractContents(value) != AirBlock::Index())
            {
                newClothVec.push_back(value);
            }
        }

        setClothes(slot, newClothVec);
        // onSlotChange((int32_t)slot);
        return MathUtils::Max(attackPower, 0.0f);
    }

    // 判断是否能穿戴改物品
    bool ComponentClothing::canWearClothing(int32_t value)
    {
        ClothingBlock *clothingBlock = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(value)]->asBlock<ClothingBlock *>();
        if (clothingBlock == nullptr)
        {
            return false;
        }
        const ClothingData &clothingData = clothingBlock->getClothingData(Terrain::ExtractData(value));

        std::vector<int32_t> clothVec = getClothes(clothingData.Slot);

        if (clothVec.size() == 0)
        {
            return true;
        }
        int32_t value2 = clothVec[clothVec.size() - 1];

        const ClothingData &clothingData2 = clothingBlock->getClothingData(Terrain::ExtractData(value2));
        return clothingData.Layer > clothingData2.Layer;
    }

    void ComponentClothing::update(float dt)
    {
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        SubsystemPickables *pickables = GameSingleton::singletonPtr->getSingleton<SubsystemPickables>();
        ClothingBlock *baseClothingBlock = GameSingleton::blocksManager->blocks[ClothingBlock::Index()]->asBlock<ClothingBlock *>();

        std::vector<int32_t> newClothVec;

        Vector3 position;
        componentBody->getPosition(position);

        // 计算等级需求
        if (componentPlayer->playerData->gameMode != GameMode::Creative && gameInfo->areAdventureSurvivalMechanicsEnabled && timeOfDay->periodicGameTimeEvent(0.5, 0.0))
        {
            for (uint8_t index = 0; index < (uint8_t)ClothingSlot::End; ++index)
            {
                bool flag = false;
                newClothVec.clear();
                std::vector<int32_t> clothCatchVec = getClothes((ClothingSlot)index);
                for (int32_t clothValue : clothCatchVec)
                {
                    const ClothingData &clothingData = baseClothingBlock->getClothingData(Terrain::ExtractData(clothValue));
                    if (clothingData.PlayerLevelRequired > componentPlayer->playerData->level)
                    {
                        pickables->addPickable(clothValue, 1, position, nullptr, nullptr);
                        flag = true;
                    }
                    else
                    {
                        newClothVec.push_back(clothValue);
                    }
                }
                if (flag)
                {
                    setClothes((ClothingSlot)index, newClothVec);
                }
            }
        }

        // 计算耐久损耗
        if (componentPlayer->playerData->gameMode != GameMode::Creative &&
            gameInfo->areAdventureSurvivalMechanicsEnabled &&
            timeOfDay->periodicGameTimeEvent(2.0, 0.0))
        {
            for (uint8_t index = 0; index < (uint8_t)ClothingSlot::End; ++index)
            {
                bool flag = false;
                newClothVec.clear();
                std::vector<int32_t> clothCatchVec = getClothes((ClothingSlot)index);
                for (int32_t clothValue : clothCatchVec)
                {
                    const ClothingData &clothingData = baseClothingBlock->getClothingData(Terrain::ExtractData(clothValue));
                    float num2 = (componentVitalStats->wetness > 0.0f) ? (10.0f * clothingData.Sturdiness) : (20.0f * clothingData.Sturdiness);
                    double num3 = MathUtils::Floor<double>(lastTotalElapsedGameTime / num2);
                    if (MathUtils::Floor<double>(gameInfo->totalElapsedGameTime / num2) > num3 && GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.75f)
                    {
                        flag = true;
                        int32_t newValue = GameSingleton::blocksManager->damageItem(clothValue, 1);
                        if (Terrain::ExtractContents(newValue) == ClothingBlock::Index())
                        {
                            newClothVec.push_back(newValue);
                        }
                    }
                }
                if (flag)
                {
                    setClothes((ClothingSlot)index, newClothVec);
                }
            }
            lastTotalElapsedGameTime = gameInfo->totalElapsedGameTime;
        }
    }

    static const std::string componentName = "Clothing";
    const std::string &ComponentClothing::getName() const
    {
        return componentName;
    }
    const std::string &ComponentClothing::GetName()
    {
        return componentName;
    }
    void ComponentClothing::load(const nlohmann::json &jsonData)
    {
        componentVitalStats = GameSingleton::gameRegistry->get<ComponentVitalStats *>(entity);
        componentBody = GameSingleton::gameRegistry->get<ComponentBody *>(entity);
        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer *>(entity);

        steedMovementSpeedFactor = 1.0f;
        insulation = 1.0f;
        lastTotalElapsedGameTime = 0.0;

        const nlohmann::json &clothesJson = jsonData["Clothes"];
        for (int32_t index = 0; index < clothes.size(); index++)
        {
            clothes[index] = clothesJson[index].get<std::vector<int32_t>>();
        }

        updateAttribute();
    }
    void ComponentClothing::save(nlohmann::json &jsonData)
    {
        nlohmann::json clothesJson;
        for (int32_t index = 0; index < clothes.size(); index++)
        {
            clothesJson[index] = clothes[index];
        }
        jsonData["Clothes"] = clothesJson;
    }

    static std::string vecIntToString(const std::vector<int32_t> vec)
    {
        std::stringstream ss;
        if (vec.size() == 0)
        {
            ss << "";
        }
        else
        {
            ss << std::to_string(vec[0]);
            for (int32_t index = 1; index < vec.size(); index++)
            {
                ss << ';';
                ss << std::to_string(vec[index]);
            }
        }
        return ss.str();
    }

    void ComponentClothing::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "Id", "int", id);
        nlohmann::json clothesJson;
        MSG_VALUE_SET(clothesJson, "Head", "string", vecIntToString(clothes[0]));
        MSG_VALUE_SET(clothesJson, "Torso", "string", vecIntToString(clothes[1]));
        MSG_VALUE_SET(clothesJson, "Legs", "string", vecIntToString(clothes[2]));
        MSG_VALUE_SET(clothesJson, "Feet", "string", vecIntToString(clothes[3]));
        jsonData["Clothes"] = clothesJson;
    }
} // namespace PocketSurvival